package dk.pun.dums.server;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import dk.pun.dums.shared.Preferences;
import dk.pun.dums.shared.character.CharacterData;
import dk.pun.dums.shared.user.LoginInfo;
import dk.pun.dums.shared.user.UserData;

@SuppressWarnings("serial")
public class DUMSServiceImpl extends RemoteServiceServlet implements dk.pun.dums.client.DUMSService {

	private static final Logger LOG = Logger.getLogger(DUMSServiceImpl.class.getName());
	private static final String ANONYMOUS_USER_ID = "1";
	private static final int ANONYMOUS_EXPIRATION = 24*60*60*1000;
	
	@Override
	public LoginInfo login(final String requestURL) {
		final UserService userService = UserServiceFactory.getUserService();
		final User user = userService.getCurrentUser();
		final LoginInfo loginInfo = new LoginInfo();
		
		if (user == null) {
			loginInfo.setLoggedIn(false);
			loginInfo.setLoginURL(userService.createLoginURL(requestURL));
		} else {
			loginInfo.setLoggedIn(true);
			loginInfo.setLogoutURL(userService.createLogoutURL(requestURL));
			loginInfo.setUserData(this.loadUserData(user));
			loginInfo.setUserAdmin(userService.isUserAdmin());
			LOG.fine("Login info: [userId=" + user.getUserId() + ", nickname=" + user.getNickname() + ", email=" + user.getEmail() + "]");
		}
		return loginInfo;
	}

	@Override
	public LoginInfo anonymousLogin() {
		this.cleanupAnonymousCharacters();
		final LoginInfo loginInfo = new LoginInfo();
		loginInfo.setLoggedIn(true);
		loginInfo.setAnonymous(true);
		loginInfo.setUserData(this.loadUserData(null));
		loginInfo.setLoginURL("/");
		loginInfo.setLogoutURL("/");
		LOG.fine("Login info: [userId=" + loginInfo.getUserData().getUserId() + ", nickname=" + loginInfo.getUserData().getNickname() + ", email=" + loginInfo.getUserData().getEmail() + "]");
		return loginInfo;
	}

	@Override
	public UserData getUserData(final String userId) {
		final PersistenceManager persistenceManager = PMF.getPersistenceManager();
		UserData userData = null;
		if (userId != null) {
			try {
				final Query query = persistenceManager.newQuery(UserData.class, "userId == otherUserId");
				query.declareParameters("Long otherUserId");
				@SuppressWarnings("unchecked")
				final List<UserData> results = (List<UserData>) query.execute(userId);
				if (results.isEmpty()) {
					userData = new UserData(userId);
					persistenceManager.makePersistent(userData);
				} else {
					userData = results.get(0);
				}
				userData = persistenceManager.detachCopy(userData);
			} finally {
				persistenceManager.close();
			}
			LOG.warning("Got user data: " + (userData == null ? userData : userData.toString()));
		}
		return userData;
	}

	@Override
	public void updateUserData(final UserData userData) {
		if  (userData != null) {
			final PersistenceManager persistenceManager = PMF.getPersistenceManager();
			try {
				persistenceManager.makePersistent(userData);
			} finally {
				persistenceManager.close();
			}
			LOG.warning("Updated user data: " + (userData == null ? userData : userData.toString()));
		}
	}

	@Override
	public void deleteUserData(final String userId) {
		if (userId != null) {
			final PersistenceManager persistenceManager = PMF.getPersistenceManager();
			try {
				final Query query = persistenceManager.newQuery(UserData.class, "userId == otherUserId");
				query.declareParameters("Long otherUserId");
				@SuppressWarnings("unchecked")
				final List<UserData> results = (List<UserData>) query.execute(userId);
				if (!results.isEmpty()) {
					persistenceManager.deletePersistent(results.get(0));
				}
			} finally {
				persistenceManager.close();
			}
			LOG.warning("Deleted user data: " + userId);
		}
	}

	@Override
	public List<CharacterData> findCharacterDataByUserKey(Long userKey) {
		final PersistenceManager persistenceManager = PMF.getPersistenceManager();
		List<CharacterData> results = new ArrayList<CharacterData>();
		try {
			if (userKey != null) {
				final Query query = persistenceManager.newQuery(CharacterData.class, "userKey == currentUserKey");
				query.declareParameters("Long currentUserKey");
				@SuppressWarnings("unchecked")
				List<CharacterData> queryResults = (List<CharacterData>) query.execute(userKey);
				for (CharacterData characterData : queryResults) {
					characterData = persistenceManager.detachCopy(characterData);
					results.add(characterData);
				}
				LOG.warning("Got character data list: " + results.toString());
			}
		} finally {
			persistenceManager.close();
		}
		return results;
	}

	@Override
	public CharacterData createCharacterData(final String name, final Long userKey) {
		final PersistenceManager persistenceManager = PMF.getPersistenceManager();
		CharacterData characterData = null;
		if (name != null) {
			characterData = new CharacterData();
			characterData.setName(name);
			characterData.setUserKey(userKey);
			try {
				persistenceManager.makePersistent(characterData);
				characterData = persistenceManager.detachCopy(characterData);
				LOG.warning("Created new character data:" + (characterData == null ? characterData : characterData.toString()));
			} finally {
				persistenceManager.close();
			}
		}
		return characterData;
	}

	@Override
	public CharacterData getCharacterData(final Long characterKey) {
		final PersistenceManager persistenceManager = PMF.getPersistenceManager();
		CharacterData characterData = null;
		try {
			if (characterKey == null) {
				characterData = new CharacterData();
				persistenceManager.makePersistent(characterData);
				LOG.warning("Created new character data:" + (characterData == null ? characterData : characterData.toString()));
			} else {
				characterData = persistenceManager.getObjectById(CharacterData.class, characterKey);
				LOG.warning("Got character data: " + (characterData == null ? characterData : characterData.toString()));
			}
			characterData = persistenceManager.detachCopy(characterData);
		} finally {
			persistenceManager.close();
		}
		return characterData;
	}

	@Override
	public void updateCharacterData(final CharacterData characterData) {
		if (characterData != null) {
			final PersistenceManager persistenceManager = PMF.getPersistenceManager();
			try {
				final CharacterData fetchedCharacterData = persistenceManager.getObjectById(CharacterData.class, characterData.getKey());
				fetchedCharacterData.copyContent(characterData);
			} finally {
				persistenceManager.close();
			}
			LOG.warning("Updated character data: " + (characterData == null ? characterData : characterData.toString()));
		}
	}
	
	@Override
	public void deleteCharacterData(final Long characterKey) {
		if (characterKey != null) {
			final PersistenceManager persistenceManager = PMF.getPersistenceManager();
			try {
				final CharacterData fetchedCharacterData = persistenceManager.getObjectById(CharacterData.class, characterKey);
				if (fetchedCharacterData != null) {
					persistenceManager.deletePersistent(fetchedCharacterData);
				}
			} finally {
				persistenceManager.close();
			}
			LOG.warning("Deleted character data: " + characterKey);
		}
	}

	@Override
	public String getMessage() {
		final PersistenceManager persistenceManager = PMF.getPersistenceManager();
		String message = "";
		try {
			final Query query = persistenceManager.newQuery(Preferences.class);
			@SuppressWarnings("unchecked")
			final List<Preferences> results = (List<Preferences>) query.execute();
			if (!results.isEmpty()) {
				final Preferences preferences = results.get(0);
				message = preferences.getMessage();
			}
		} finally {
			persistenceManager.close();
		}
		return message;
	}

	@Override
	public void setMessage(final String message) {
		if (message != null) {
			final PersistenceManager persistenceManager = PMF.getPersistenceManager();
			try {
				final Query query = persistenceManager.newQuery(Preferences.class);
				@SuppressWarnings("unchecked")
				final List<Preferences> results = (List<Preferences>) query.execute();
				if (results.isEmpty()) {
					final Preferences preferences = new Preferences();
					preferences.setMessage(message);
					persistenceManager.makePersistent(preferences);
				} else {
					final Preferences preferences = results.get(0);
					preferences.setMessage(message);
				}
			} finally {
				persistenceManager.close();
			}
		}
	}

	private UserData loadUserData(final User user) {
		String userId;
		if (user == null) {
			userId = ANONYMOUS_USER_ID;
		} else {
			userId = user.getUserId();
		}
		UserData userData = this.getUserData(userId);
		if (user == null) {
			userData.setNickname("Anonym");
		} else {
			userData.setNickname(user.getNickname());
			userData.setEmail(user.getEmail());
		}
		this.updateUserData(userData);
		return userData;
	}

	private void cleanupAnonymousCharacters() {
		final PersistenceManager persistenceManager = PMF.getPersistenceManager();
		try {
			final Query query = persistenceManager.newQuery(UserData.class, "userId == currentUserId");
			query.declareParameters("String currentUserId");
			@SuppressWarnings("unchecked")
			final List<UserData> results = (List<UserData>) query.execute(ANONYMOUS_USER_ID);
			if (results.size() > 0) {
				final Date expirationTime = new Date(System.currentTimeMillis() - ANONYMOUS_EXPIRATION);
				final UserData userData = results.get(0);
				final List<CharacterData> characterDataList = this.findCharacterDataByUserKey(userData.getKey());
				for (CharacterData characterData : characterDataList) {
					if (characterData.getCreatedDate().before(expirationTime)) {
						persistenceManager.deletePersistent(characterData);
					}
				}
			}
		} finally {
			persistenceManager.close();
		}
	}
}
